1. Introduction : importance des patrons
Science is what we understand well enough to explain to a computer. Art is everything else we do.
1.1. Strategy
1.1.1. Principes de conception
|
Principe de conception
|
|
Principe de conception
|
|
Principe de conception
|
1.1.2. Définition du patron
|
Design pattern : Stratégie (Strategy)
Stratégie définit une famille d’algorithmes, encapsule chacun d’eux et les rend interchangeables. Il permet à l’algorithme de varier indépendamment des clients qui l’utilisent. Figure 2. Modèle UML du patron Strategy
|
1.1.3. Premier exemple d’utilisation
|
Question
Pourquoi n’a-t’on pas utilisé Strategy pour afficher() ou nager()?
|
1.1.4. Autre exemple concret
| L’exemple qui suit est tiré de ce cours. |
Le problème
Vous avez une classe FileWriter qui a pour rôle d’écrire dans un fichier
ainsi qu’une classe DBWriter. Dans un premier temps, ces classes ne
contiennent qu’une méthode write() qui n’écrira que le texte passé
en paramètre.
Au fil du temps, vous vous rendez compte que c’est dommage qu’elles ne fassent que ça et vous aimeriez bien qu’elles puissent écrire en différents formats (HTML, XML, etc.) : les classes doivent donc formater puis écrire.
La solution
|
L’interface en
PHP (code source ici)
|
|
La classe abstraite
Writer (code source ici)
|
|
La classe
FileWriter (code source ici)
|
|
La classe
DBWriter (code source ici)
|
Enfin, nous avons nos trois formateurs.
L’un ne fait rien de particulier (TextFormater),
et les deux autres formatent le texte en deux langages
différents (HTMLFormater et XMLFormater).
|
La classe
TextFormater (code source ici)
La classe
HTMLFormater (code source ici)
La classe
XMLFormater (code source ici)
|
1.1.5. D’autres exemples
-
La fonction standard
sort()de python>>> sorted("This is a test string from Andrew".split(), key=str.lower) ['a', 'Andrew', 'from', 'is', 'string', 'test', 'This'] -
Stratégie de cryptage en fonction de la taille d’un fichier
File file = getFile(); Cipher c = CipherFactory.getCipher( file.size() ); c.performAction(); // implementations: interface Cipher { public void performAction(); } class InMemoryCipherStrategy implements Cipher { public void performAction() { // load in byte[] .... } } class SwaptToDiskCipher implements Cipher { public void performAction() { // swapt partial results to file. } }
| Plus de détails ici |
1.2. (non) Réutilisation
| Les patrons ne sont pas réutilisables! |
Il faut implémeter la solution qu’il représente à chaque fois.
Exception : certains font l’objet d’une librairie (comme Observer de Java).
Par exemple le patron Singleton existe dans la bibliothèque standard du langage en Ruby. C’est un mixin qu’il suffit d’inclure dans la classe qui doit être un singleton.
class Klass
include Singleton
# ...
end
a,b = Klass.instance, Klass.instance
a == b
# => true
Klass.new
# => NoMethodError - new is private ...
1.3. Association ou composition
On trouve deux modèles UML™ :
Et donc deux implémentations :
public class Colvert extends Canard {
protected Colvert() {
this(new VolerAvecDesAiles(), new Cancan());
}
...
c1 = new Colvert();
...
vol = new VolerAvecDesAiles();
cri = new Cancan();
c1 = new Colvert(vol,cri);
...
2. Un peu d’histoire
- 1977
-
Alexander : patterns pour les architectures (les vraies)
- 1987
-
Beck et Cunningham : patterns pour des interfaces utilisateurs
- 1988
-
Meyer : livre sur l’orienté objet (langage Eiffel), devenu la bible pour beaucoup de programmeurs (cf. [Meyer88])
- 1990-1995
-
Gamma, Helm, Johnson et Vlissides : LE livre de référence (cf. [GoF])
Les auteurs de ce livre sont connus comme les Gof pour « Gang of Four ». - 2003
-
Martin : principes SOLID (cf. [Martin03])
- 2004
-
Craig Larman décrit des modèles de conception : les Patterns GRASP (cf. [Larman05])
3. Exemples de bons principes
SOLID:
-
Single Responsibility Principle
-
Open-Closed Principle
-
Liskov Substitution Principle
-
Interface Segregation Principle
-
Dependency Inversion Principle
3.1. Single Responsibility Principle
Responsabilité => Sujet à changement
3.2. Open-Closed Principle
Ouvert à l'extension mais fermé à la modification
Ainsi, une fois écrite et testée, une classe ne devrait être modifiée que pour être corrigée! Toute modification devrait être possible par extension.
3.3. Liskov Substitution Principle
Barbara Liskov, pionnière en informatique et plus précisément en OO, a donné son nom à un principe important et bien connu : le principe default: substitution de Liskov. Elle a reçu l’équivalent du prix Nobel d’Informatique (le Turing Award) en 2009.
Une classe doit pouvoir être remplacée par une instance d'un de ses sous-types, sans modifier la cohérence du programme
Un carré est un rectangle particulier.
|
Question
Peut-on toujours substituer un Carré à la place d’un Rectangle ?
|
Rectangle.java)class Rectangle
{
protected int m_width;
protected int m_height;
public void setWidth(int width){
m_width = width;
}
public void setHeight(int height){
m_height = height;
}
public int getWidth(){
return m_width;
}
public int getHeight(){
return m_height;
}
public int getArea(){
return m_width * m_height;
}
}
Square.java)// Violation of Likov's Substitution Principle
class Square extends Rectangle
{
public void setWidth(int width){
m_width = width;
m_height = width;
}
public void setHeight(int height){
m_width = height;
m_height = height;
}
}
Square.java - suite)class LspTest
{
private static Rectangle getNewRectangle()
{
// it can be an object returned by some factory ...
return new Square();
}
public static void main (String args[])
{
Rectangle r = LspTest.getNewRectangle();
r.setWidth(5);
r.setHeight(10);
// User knows that r is a rectangle.
// It assumes that he's able to set the width and height as for the base class
System.out.println(r.getArea());
// Now she's surprised to see that the area is 100 instead of 50.
}
}
Rectangle.java)class LspTest
{
private static Square getNewSquare()
{
// it can be an object returned by some factory ...
return new Rectangle();
}
public static void main (String args[])
{
Square s = LspTest.getNewSquare();
s.setWidth(5);
// User knows that r is a rectangle.
// It assumes that he's able to set the width and height as for the base class
System.out.println(s.getArea());
// Now she's surprised to see that the area is 0 instead of 25.
}
}
3.4. Interface Segregation Principle
Préférer plusieurs interfaces spécifiques pour chaque client plutôt qu'une seule interface générale
3.5. Dependency Inversion Principle
Il faut dépendre des abstractions, pas des implémentations
Ce principe indique :
-
Les modules de haut niveau (abstraits) ne doivent pas dépendre des modules de bas niveau. Les deux doivent dépendre d’abstractions.
-
Les abstractions ne doivent pas dépendre des détails d’implémentation. C’est l’inverse : les détails doivent dépendre des abstractions.
| Ainsi ce principe va à l’encontre de l’intuition classique. |
3.6. SOLID et patrons
|
QUESTION
Lesquels des 5 principes SOLID s’appliquent bien à Strategy ? |
3.7. GRASP
The critical design tool for software development is a mind well educated in design principles. It is not the UML or any other technology.
2005
Il s’agit d’un ensemble de patrons, plutôt orientés conception (UML). Nous en aborderons certains au travers des exemples de ce module (cf. [Larman05]).
| Notez que les principes SOLID ne s’appliquent pas qu’à la programmation objet. Pour une discussion sur leur application avec React (language fonctionnel), cf. https://dev.to/shadid12/can-you-apply-solid-principles-to-your-react-applications-46il. |
4. Les patrons : comment ça marche ?
4.1. Intérêt
-
Réponses éprouvées à des problèmes récurrents
-
Vocabulaire commun
T’as qu’à utiliser une factory!
4.3. Patrons non abordés
-
Décorateur
-
Commande
-
Façade
-
Patron de méthode
-
Chaînes de responsabilité
-
Prototype
-
Mémento
-
Médiateur
-
Interprète
-
Poids-mouche
-
Monteur
-
Pont